package com.bff.slimserver.musicjukebox.ui.components;

import com.bff.slimserver.utils.TableSorter;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

public class TableColumnManager implements MouseListener, ActionListener {
    //

    private static final String HIDDEN_COLUMN = "HiddenColumn:";
    //Reference to the Singleton instance of this class
    private static TableColumnManager singleton;

    /**
     *  Use a private constructor so we can create a Singleton.
     */
    private TableColumnManager() {
    }

    /**
     *  Get the Singleton instance of the TableColumnManager.
     *
     *  @return  the Singlton instance of TableColumnManager
     */
    private static TableColumnManager getInstance() {
        if (singleton == null) {
            singleton = new TableColumnManager();
        }

        return singleton;
    }

    /**
     *  Add support for the management of visible TableColumns
     *  for a given JTable.
     *
     *  @param  table the table to provide support for
     */
    public static void install(JTable table) {
        table.getTableHeader().addMouseListener(getInstance());
    }

    /**
     *  Remove support for the management of visible TableColumns
     *  for a given JTable.
     *
     *  @param  table the table to remove support from
     */
    public static void uninstall(JTable table) {
        table.getTableHeader().removeMouseListener(getInstance());
    }

    /**
     *  Hide a column from view in the table.
     *
     *  @param  table        the table from which the column is removed
     *  @param  modelColumn  the column index from the TableModel
     *                       of the column to be removed
     */
    public static void hideColumn(JTable table, int modelColumn) {
        int viewColumn = table.convertColumnIndexToView(modelColumn);

        if (viewColumn != -1) {
            TableColumn column = table.getColumnModel().getColumn(viewColumn);
            hideColumn(table, column);
        }
    }

    /**
     *  Hide a column from view in the table.
     *
     *  @param  table        the table from which the column is removed
     *  @param  columnName   the column name of the column to be removed
     */
    public static void hideColumn(JTable table, String columnName) {
        try {
            TableColumnModel tcm = table.getColumnModel();
            int index = tcm.getColumnIndex(columnName);
            TableColumn column = tcm.getColumn(index);
            hideColumn(table, column);
        } catch (IllegalArgumentException e) {
        }
    }

    /**
     *  Hide a column from view in the table.
     *
     *  @param  table   the table from which the column is removed
     *  @param  column  the TableColumn to be removed from the
     *                  TableColumnModel of the specified table
     */
    public static void hideColumn(JTable table, TableColumn column) {
        table.getColumnModel().removeColumn(column);

        //  Save the column so it can be redisplayed

        ArrayList list = (ArrayList) table.getClientProperty(HIDDEN_COLUMN);

        if (list == null) {
            list = new ArrayList();
            table.putClientProperty(HIDDEN_COLUMN, list);
        }

        list.add(column);
    }

    /**
     *  Show a hidden column in the table.
     *
     *  @param  table        the table to which the column is added
     *  @param  modelColumn  the column index from the TableModel
     *                       of the column to be added
     */
    public static void showColumn(JTable table, int modelColumn) {
        ArrayList list = (ArrayList) table.getClientProperty(HIDDEN_COLUMN);

        if (list == null) {
            return;
        }

        ListIterator it = list.listIterator();

        while (it.hasNext()) {
            TableColumn column = (TableColumn) it.next();

            if (column.getModelIndex() == modelColumn) {
                table.getColumnModel().addColumn(column);
                it.remove();
                break;
            }
        }
    }

    /**
     *  Show a hidden column in the table.
     *
     *  @param  table        the table to which the column is added
     *  @param  columnName   the column name from the TableModel
     *                       of the column to be added
     */
    public static void showColumn(JTable table, String columnName) {
        ArrayList list = (ArrayList) table.getClientProperty(HIDDEN_COLUMN);

        if (list == null) {
            return;
        }

        ListIterator it = list.listIterator();

        while (it.hasNext()) {
            TableColumn column = (TableColumn) it.next();

            if (column.getHeaderValue().equals(columnName)) {
                table.getColumnModel().addColumn(column);
                it.remove();
                break;
            }
        }
    }

    //  Implement the MouseListener
    public void mousePressed(MouseEvent e) {
        checkForPopup(e);
    }

    public void mouseReleased(MouseEvent e) {
        checkForPopup(e);
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    private void checkForPopup(MouseEvent e) {
        if (e.isPopupTrigger()) {
            JTableHeader header = (JTableHeader) e.getComponent();
            int column = header.columnAtPoint(e.getPoint());
            showPopup(header.getTable(), column);
        }
    }

    /**
     *  Show a hidden column in the table.
     *
     *  @param  table        the table to which the column is added
     *  @param  columnName   the column name from the TableModel
     *                       of the column to be added
     */
    public static void showPopup(JTable table, int tableColumn) {
        int columns = table.getModel().getColumnCount();
        JCheckBoxMenuItem[] items = new JCheckBoxMenuItem[columns];

        //  Create menu items for hidden columns

        ArrayList list = (ArrayList) table.getClientProperty(HIDDEN_COLUMN);

        if (list != null) {
            ListIterator it = list.listIterator();

            while (it.hasNext()) {
                TableColumn column = (TableColumn) it.next();
                String columnName = column.getHeaderValue().toString();
                JCheckBoxMenuItem item = new JCheckBoxMenuItem(columnName);
                item.setSelected(false);
                item.addActionListener(getInstance());
                items[column.getModelIndex()] = item;
            }
        }

        //  Create menu items for visible columns

        TableColumnModel columnModel = table.getColumnModel();
        int columnCount = columnModel.getColumnCount();

        for (int i = 0; i < columnCount; i++) {
            TableColumn column = columnModel.getColumn(i);
            String columnName = column.getHeaderValue().toString();
            JCheckBoxMenuItem item = new JCheckBoxMenuItem(columnName);
            item.setSelected(true);
            item.addActionListener(getInstance());
            items[column.getModelIndex()] = item;
        }

        //  Create popup menu from array of menu items.
        //  Override method to select the specified menu item.
        //  (ie. this is a bug? fix)

        JPopupMenu popup = new JPopupMenu() {

            public void setSelected(Component sel) {
                int index = getComponentIndex(sel);
                getSelectionModel().setSelectedIndex(index);
                final MenuElement me[] = new MenuElement[2];
                me[0] = (MenuElement) this;
                me[1] = getSubElements()[index];

                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        MenuSelectionManager.defaultManager().setSelectedPath(me);
                    }
                });
            }
        };

        for (int i = 0; i < items.length; i++) {
            if (items[i] != null) {
                popup.add(items[i]);
            }
        }

        //  Display the popup below the TableHeader
        JTableHeader header = table.getTableHeader();
        Rectangle r = header.getHeaderRect(tableColumn);
        popup.show(header, r.x, r.height);
        int modelColumn = table.convertColumnIndexToModel(tableColumn);
        popup.setSelected(items[modelColumn]);
    }

    /**
     *  Responsible for processing the ActionEvent. A column will either be
     *  added to the table or removed from the table depending on the state
     *  of the menu item that was clicked.
     *
     * @param event the ActionEvent.
     */
    public void actionPerformed(ActionEvent event) {
        JMenuItem item = (JMenuItem) event.getSource();
        JPopupMenu popup = (JPopupMenu) item.getParent();
        JTableHeader header = (JTableHeader) popup.getInvoker();
        JTable table = header.getTable();

        if (item.isSelected()) {
            showColumn(table, item.getText());
        } else {
            hideColumn(table, item.getText());
        }
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(TableColumnManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            Logger.getLogger(TableColumnManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(TableColumnManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedLookAndFeelException ex) {
            Logger.getLogger(TableColumnManager.class.getName()).log(Level.SEVERE, null, ex);
        }

        TableModel tm = new DefaultTableModel(5, 10);
        tm.setValueAt("test1", 0, 0);
        tm.setValueAt("test2", 1, 0);

        TableSorter ts = new TableSorter(tm);
        JTable table = new JTable(ts);

        ts.setTableHeader(table.getTableHeader());
        
        TableColumnManager.install(table);
        TableColumnManager.hideColumn(table, 2);
        TableColumnManager.hideColumn(table, "E");

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new JScrollPane(table));
        frame.pack();

        frame.setVisible(true);
    }
}